// MultiRecordsDatabaseElement.h: interface for the MultiRecordsDatabaseElement class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_MULTIRECORDSDATABASEELEMENT_H__BD1E5E8F_2A59_4D4D_B6E6_CBEF4166088E__INCLUDED_)
#define AFX_MULTIRECORDSDATABASEELEMENT_H__BD1E5E8F_2A59_4D4D_B6E6_CBEF4166088E__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "DatabaseElement.h"
#include "ManagerTemplate.h"
#include <vector>
#include "DatabaseBase.h"
#include "UeConnection.h"
#include "UEException.h"

namespace PDMS
{
	namespace PDMSDBIF
	{
		class DllExport MultiRecordsDatabaseElement : public DatabaseElement  
		{
			DECLARE_DYNAMIC_EX( MultiRecordsDatabaseElement );
		public:
			MultiRecordsDatabaseElement();
			virtual ~MultiRecordsDatabaseElement();

		public:
			virtual void NewRecord() = 0;
			virtual void UpdateRecord(unsigned long lID) = 0;
			virtual void DeleteRecord(unsigned long lID) = 0;
		};
		
		template < typename Record >
			class DatabaseTableTemplate : public MultiRecordsDatabaseElement
		{
		protected:
			typedef Record Record_;
			typedef Record* RecordPtr_;
			typedef ManagerTemplate< unsigned long, Record_ > UpdateMap;
			typedef std::vector< RecordPtr_ > InsertArray;
			typedef ReferenceManager< unsigned long, unsigned long > DeleteMap;

		public:
			DatabaseTableTemplate(){
				m_pCurRecord = NULL;
			}
			virtual ~DatabaseTableTemplate(){}

		public:
			virtual void NewRecord() {
				m_pCurRecord = new Record_;
				m_arInserts.push_back( m_pCurRecord );
			}

			virtual void UpdateRecord( unsigned long lID ) {
				if ( m_mapUpdates.IsExist( lID ) )
				{
					m_pCurRecord = m_mapUpdates.GetManaged( lID );
					return;
				}
				m_pCurRecord = new Record_;
				m_pCurRecord->SetID( lID );
				m_mapUpdates.Add( lID, m_pCurRecord );
			}

			virtual void DeleteRecord( unsigned long lID ) {
				if ( m_mapDels.IsExist( lID ) )
					return;
				m_mapDels.Add( lID, lID );
			}

			virtual UESoft::ErrorStatus Close()
			{
				DatabaseElement::Close();
				ClearAll();
				try
				{
					if (Recordset() != NULL )
					{
						Recordset()->Close();
						m_ResPtr = NULL;
					}
				}
				catch ( UEException* pE )
				{
					pE->ReportError();
					pE->Delete();
					return UESoft::eFileAccessErr;
				}
				return UESoft::eOk;
			}
			
			virtual UESoft::ErrorStatus Update()
			{
				UESoft::ErrorStatus es = UESoft::eOk;

				try
				{
					es = ExecuteDelete();
					if ( es != UESoft::eOk )
					{
						return es;
					}
					es = ExecuteUpdate();
					if ( es != UESoft::eOk )
					{
						return es;
					}
					es = ExecuteInsert();
					if ( es != UESoft::eOk )
					{
						return es;
					}
					if ( m_ResPtr != NULL )
						m_ResPtr->BatchUpdate();
				}
				catch ( UEException* pE )
				{
					pE->ReportError();
					pE->Delete();
					return UESoft::eFileAccessErr;
				}
				ClearAll();

				return UESoft::eOk;
			}

		private:
			void ClearAll() {
				InsertArray::iterator it;
				for ( it = m_arInserts.begin(); it != m_arInserts.end(); it++ )
				{
					delete (*it);
				}
				m_arInserts.clear();
				
				m_mapUpdates.DeleteAll();
				m_mapDels.DeleteAll();
			}

			UESoft::ErrorStatus ExecuteDelete() {
				if ( m_mapDels.GetElementCount() == 0 )
					return UESoft::eOk;
				
				StdString strSQL( _T("delete * from ") + GetTableName() );
				StdString strCondition( _T(" where ") );
				StdString strTemp;

				DeleteMap::IteratorPtr ptr = m_mapDels.NewIterator();
				UINT nCount = 0;
				for ( ptr->Begin(); !ptr->End(); ptr->Next() )
				{
					strTemp.Format( _T(" ID = %ld or"), ptr->Get() );
					strCondition += strTemp;
					
					nCount++;
					if ( nCount == 250 )
					{
						strCondition.TrimRight( _T("or") );						
						StdString strTemp = strSQL + strCondition;						
						GetDatabase()->DataSource()->ExecuteNoSelectSQL( strTemp );
						strCondition = _T(" where ");
						nCount = 0;
						continue;
					}
				}
				if ( nCount == 0 )
					return UESoft::eOk;

				strCondition.TrimRight( _T("or") );						
				strSQL += strCondition;						
				GetDatabase()->DataSource()->ExecuteNoSelectSQL( strSQL );

				return UESoft::eOk;
			}

			UESoft::ErrorStatus ExecuteInsert() {				
				InsertArray::iterator it;
				if ( m_arInserts.size() > 0 && m_ResPtr == NULL )
				{
					StdString strSQL( _T("Select top 1 * from ") + GetTableName() );
					try
					{
						m_ResPtr = GetDatabase()->DataSource()->ExecuteSelectSQL( strSQL );
					}
					catch ( UEException* pE )
					{
						pE->ReportError();
						pE->Delete();
						return UESoft::eFileAccessErr;
					}
				}
				for ( it = m_arInserts.begin(); it != m_arInserts.end(); it++ )
				{
					if ( m_mapDels.IsExist( (*it)->GetID() ) )
						continue;
					m_ResPtr->NewRecord();
					SetData( *it );
				}
				return UESoft::eOk;
			}
			
			UESoft::ErrorStatus ExecuteUpdate() {
				if ( m_mapUpdates.GetElementCount() == 0 )
				{
					return UESoft::eOk;
				}
				UpdateMap::IteratorPtr ptr = m_mapUpdates.NewIterator();
				
				StdString strSQL( _T("Select * from ") + GetTableName() );
				StdString strCondition( _T(" where ") );
				StdString strTemp;
				
				UINT nCount = 0;
				for ( ptr->Begin(); !ptr->End(); ptr->Next() )
				{
					strTemp.Format( _T(" ID = %ld or"), ptr->Get()->GetID() );
					strCondition += strTemp;
					
					nCount++;
					if ( nCount == 250 )
					{
						strCondition.TrimRight( _T("or") );						
						strSQL += strCondition;						
						m_ResPtr = GetDatabase()->DataSource()->ExecuteSelectSQL( strSQL );
						
						RecordPtr_ pRecord = NULL;
						if ( m_ResPtr->IsBOF() && m_ResPtr->IsEOF() )
							return UESoft::eOk;
						
						m_ResPtr->MoveFirst();
						while ( !m_ResPtr->IsEOF() && !m_ResPtr->IsBOF() )
						{
							pRecord = m_mapUpdates.GetManaged( m_ResPtr->GetLong( _T("ID") ) );
							ASSERT( pRecord != NULL );
							
							SetData( pRecord );
							m_ResPtr->MoveNext();
						}
						nCount = 0;
						m_ResPtr->Update();
						continue;
					}
				}
				
				if ( nCount == 0 )
					return UESoft::eOk;
				
				strCondition.TrimRight( _T("or") );						
				strSQL += strCondition;						
				m_ResPtr = GetDatabase()->DataSource()->ExecuteSelectSQL( strSQL );
				
				RecordPtr_ pRecord = NULL;
				if ( m_ResPtr->IsBOF() && m_ResPtr->IsEOF() )
					return UESoft::eOk;
				
				m_ResPtr->MoveFirst();
				while ( !m_ResPtr->IsEOF() && !m_ResPtr->IsBOF() )
				{
					pRecord = m_mapUpdates.GetManaged( m_ResPtr->GetLong( _T("ID") ) );
					ASSERT( pRecord != NULL );
					
					SetData( pRecord );
					m_ResPtr->MoveNext();
				}
				return UESoft::eOk;
			}

		protected:
			virtual StdString GetTableName() = 0;
			virtual void SetData( RecordPtr_ ptr ) = 0;

		protected:
			RecordPtr_ m_pCurRecord;

		private:
			InsertArray m_arInserts;
			DeleteMap m_mapDels;
			UpdateMap m_mapUpdates;
		};
	}
}

#endif // !defined(AFX_MULTIRECORDSDATABASEELEMENT_H__BD1E5E8F_2A59_4D4D_B6E6_CBEF4166088E__INCLUDED_)
